/* This is a pretty basic vector class. Vectors of any (positive non
 * zero) length are permitted. When this class is completed, any vector
 * will be scalable or normalizable and can return its own length and
 * magnitude. Any pair of vectors can be added, subtracted, or dotted.
 * Currently,the cross product is only defined for a 3D vector.
 *
 * Written by Greg Meece, Fall 2012.
 */

#ifndef VEC_H
#define VEC_H

#include <cmath>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <ostream>
#include <exception>

using namespace std;

class IllegalVectorOperationException:public exception
{
   virtual const char* what() const throw()
   {
      return "Tried to perform an illegal vector operation.";
   }
};

 
using namespace std;

class vec
{
public:

   // Default constructor. Creates an empty vector. Attempting to
   // access anything will cause issues.
   vec ()
   {
      data = NULL;
   }

   // Create a new vector with the desired (positive, non zero) length.
   // The vector is initially filled with zeros.
   vec(int len)
   {
      if (len > 0) {
         data = new double[len];

         for (int i = 0; i < len; i++)
            data[i] = 0.0;

         dim = len;
            
         }

      else {
         cout << "Error! Trying to create a vector with an invalid";
         cout << " length " <<  len << endl;
         
         throw(IllegalVectorOperationException());
         }
   }
   
   // Copy constructor.
   vec(const vec &v)
   {
      dim = v.len();
      data = new double[dim];
      
      for (int i = 0; i < dim; i++) {
         data[i] = v[i];
         }
   }

   // Return the dimensionality of the vector.
   int len() const
   {
      return dim;
   }
   
   // Return the magnitude of the vector.
   double mag() const
   {
      double mag = 0.0;
      
      for (int i = 0; i < dim; i++)
         mag += data[i] * data[i];
      
      mag = sqrt(mag);
      
      return mag;
   }
   
   // Returns a normalized version of the vector. Does not change the
   // actual vector.
   vec normalize() const
   {
      vec nv(dim);
      double m = mag();
      
      for (int i = 0; i < dim; i++)
         nv[i] = data[i] / m;
         
      return nv;
   }
   
   // Takes the inner product of two vectors and returns the result.
   // Returns a dot b, where this is a and the passed parameter is b.
   // Does not change either vector.
   
   double dot(vec b) const
   {
      double sum = 0.0;
      
      if (dim != b.len())
         throw(IllegalVectorOperationException());
      
      for (int i = 0; i < dim; i++)
         sum += data[i] * b[i];
         
      return sum;
   }
   
   // Computes the outer product of two vectors. Returns the result as
   // a vector. Only defined for 3 dimensional vectors. Once again,
   // this function returns a cross b, where a is this vector and b is
   // the passed parameter.
   vec cross(vec b) const
   {
      vec c(3);
      
      if (dim != 3 && b.len() != 3)
         throw(IllegalVectorOperationException());
     
      c[0] = data[1]*b[2] - data[2]*b[1];
      c[1] = data[2]*b[0] - data[0]*b[2];
      c[2] = data[0]*b[1] - data[1]*b[0];
     
      return c;
   }
   
   // Destructor for a vector. Needed since the array to hold the data
   // is dynamically allocated.
   ~vec()
   {
      delete data;
   }
   
   // The bracket operator needs to support both querries and
   // assignment. To do this, it returns the value by reference.
   double& operator[](int i) const
   {
      return data[i];
   }
   
   // The addition operator is overloaded to return the sum of two
   // vectors. The inputs are not changed.
   vec operator+(vec b) const
   {
      if (dim != b.len()) {
         cout << "Add" << endl;
         throw(IllegalVectorOperationException());
         }
         
      vec c(dim);
      
      for (int i = 0; i < dim; i++)
         c[i] = data[i] + b[i];
         
      return c;
   }

   // The subtraction operator is overloaded to return the difference
   // of two vectors. The inputs are not changed.
   vec operator-(vec b) const
   {
      if (dim != b.len())
         throw(IllegalVectorOperationException());
         
      vec c(dim);
      
      for (int i = 0; i < dim; i++)
         c[i] = data[i] - b[i];
         
      return c;
   }

   // The multiplication operator is overloaded to scale a vector
   // by a constant. The inputs are not changed.
   vec operator*(double c) const
   {
      vec v(dim);

      for(int i = 0; i < dim; i++)
         v[i] = data[i] * c;

      return v;
   }
   
   // Overload the assignment operator in order to make deep copies
   // of the data rather than merely copying addresses.
   vec& operator=(const vec& r)
   {
      if (this != &r) {
        // if (data != NULL && dim != r.len()) {
         //   cout << "Can not copy a vector to a vector of a different"
          //  << " size." << endl;
          //  throw(IllegalVectorOperationException());
           // }

	 if (data == NULL) {
	    data = new double[r.len()];
	    dim = r.len();
	    }

         for (int i = 0; i < dim; i++)
            data[i] = r[i];
         }
         
      return *this;
   }

   // Prints the components of the vector to a specified output stream
   // in columns of the specified width with the specified precision.
   void printToStream(ostream & of, int w, double pr) {
      for (int i = 0; i < dim; i++) {
         of << setw(w) << setprecision(pr) << data[i];
         }

      of << endl;
   }

private:
  double* data;
  int dim;
};

#endif // VEC_H
